/**
 * 
 */
package cs534.crf;

import java.util.Arrays;


/**
 * @author jhostetler
 *
 */
public final class ConditionalRandomField<Observation>
{	
	public abstract static class Clique<Observation>
	{
		public final int[] vertices;
		
		public Clique( final int[] vertices )
		{
			this.vertices = vertices;
		}
		
		/**
		 * 
		 * @param x
		 * @param y The values of the label vertices. The vertices must be
		 * given in the same order as they appear in the constructor. It is
		 * strongly encouraged to use the same order as the corresponding CRF. 
		 * @return
		 */
		public abstract double logPotential( final Observation x, final int[] y );
		
		public abstract double[] parameters();
		
		public abstract int numParameters();
		
		public abstract Clique<Observation> update( final double[] parameters );
	}
	
	public final int Nvertices;
	public final int Ncliques;
	
	private final int[] cardinalities_;
	private final int[][] adjacency_list_;
	private final Clique<Observation>[] cliques_;
	private final int[][] vertex_cliques_;
	
	/**
	 * Constructor.
	 * @param cardinalities The cardinalities of each vertex.
	 * @param adjacency_list First index: vertex; second index: neighbors
	 * @param cliques The cliques defining the distribution.
	 */
	public ConditionalRandomField( final int[] cardinalities,
								   final int[][] adjacency_list, 
								   final Clique<Observation>[] cliques )
	{
		cardinalities_ = cardinalities;
		adjacency_list_ = adjacency_list;
		Nvertices = cardinalities_.length;
		assert( adjacency_list_.length == Nvertices );
		
		cliques_ = cliques;
		Ncliques = cliques_.length;
		
		int[] sizes = new int[Nvertices];
		for( int i = 0; i < Ncliques; ++i ) {
			for( int v : cliques_[i].vertices ) {
				sizes[v] += 1;
			}
		}
		
		vertex_cliques_ = new int[Nvertices][];
		for( int v = 0; v < Nvertices; ++v ) {
			vertex_cliques_[v] = new int[sizes[v]];
		}
		
		for( int c = 0; c < Ncliques; ++c ) {
			for( int v : cliques_[c].vertices ) {
				vertex_cliques_[v][vertex_cliques_[v].length - sizes[v]] = c;
				sizes[v] -= 1;
			}
		}
		for( int v = 0; v < Nvertices; ++v ) {
			assert( sizes[v] == 0 );
		}
	}
	
	public int cardinality( final int v )
	{
		return cardinalities_[v];
	}
	
	public int[] cardinalities()
	{
		return cardinalities_;
	}
	
	public int[] neighbors( final int v )
	{
		return adjacency_list_[v];
	}
	
	public int[][] adjacency_list()
	{
		return adjacency_list_;
	}
	
	public Clique<Observation>[] cliques()
	{
		return cliques_;
	}
	
	public int[] cliques( final int vertex )
	{
		return vertex_cliques_[vertex];
	}
	
	public int numParameters()
	{
		int result = 0;
		for( final Clique<Observation> clique : cliques_ ) {
			result += clique.numParameters();
		}
		return result;
	}
	
}
